package com.moonshineproject.text
{
	import com.moonshineproject.vo.Settings;
	
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.text.engine.ContentElement;
	import flash.text.engine.ElementFormat;
	import flash.text.engine.GroupElement;
	import flash.text.engine.TabAlignment;
	import flash.text.engine.TabStop;
	import flash.text.engine.TextBlock;
	import flash.text.engine.TextElement;
	import flash.text.engine.TextLine;
	import flash.utils.Timer;
	
	
	public class TextLineRenderer extends Sprite
	{	
		// This needs to derive from the font size
		public static var lineHeight:Number = 16;
		
		private static var numTabstops:int = 100;
		private static var tabStops:Vector.<TabStop>;
		
		classInit(); // Do static init once & once only.
		static private function classInit():void {
			tabStops = new Vector.<TabStop>(numTabstops);
			for (var i:int = 0; i < numTabstops; i++) {
				tabStops[i] = new TabStop(TabAlignment.START, Math.round((i+1)*7.85*Settings.font.tabWidth));
			}
		}
		
		public var styles:Object; 
		public var lineNumberWidth:int;
																
		private var textBlock:TextBlock = new TextBlock();
		private var textLine:TextLine;
		
		private var lineNumberTextBlock:TextBlock = new TextBlock();
		private var lineNumberText:TextLine;
		private var lineNumberBackground:Sprite;
		
		private var marker:Sprite;
		private var markerBlinkTimer:Timer;
		private var lastMarkerPosition:Number;
		
		private var selection:Sprite;
		
		
		private var _dataIndex:int;
		public function get dataIndex():int
		{
			return _dataIndex;
		}
		public function set dataIndex(v:int):void
		{
			_dataIndex = v;
			
			drawLineNumber();
		}
		
		
		private var _model:TextLineModel;
		public function get model():TextLineModel
		{
			return _model;
		}
		public function set model(v:TextLineModel):void 
		{
			focus = false;
			
			_model = v;
			
			drawText();
		}
		
		private var _horizontalOffset:int = 0;
		public function get horizontalOffset():int
		{
			return _horizontalOffset;
		}
		public function set horizontalOffset(v:int):void
		{
			_horizontalOffset = v;
			if (textLine)
			{
				textLine.x = lineNumberWidth + _horizontalOffset;
				selection.x = lineNumberWidth + _horizontalOffset;
				drawMarkerAtPosition(lastMarkerPosition, 0);
			}	
		}
		
		private var _caretPosition:int;
		public function get caretPosition():int
		{
			return _caretPosition;
		}
		public function set caretPosition(charIndex:int):void
		{
			_caretPosition = charIndex;
			drawCaret(charIndex);
		}
		
		private var _focus:Boolean = false;
		public function get focus():Boolean
		{
			return _focus;
		}
		public function set focus(v:Boolean):void
		{
			_focus = v;
			if (v)
			{
				markerBlinkTimer.start();
				marker.visible = true;
				
				// Background line fill
				graphics.clear();
				graphics.beginFill(styles['selectedLineColor'], styles['selectedLineColorAlpha']);
				graphics.drawRect(lineNumberWidth, 0, 2000, lineHeight); 
				graphics.endFill();
			}
			else
			{
				graphics.clear();	
				markerBlinkTimer.reset();
				marker.visible = false;	
			}
		}
		
		public function TextLineRenderer()
		{
			super();
			init();
		}
		
		private function init():void 
		{
			selection = new Sprite();
			addChild(selection);
			
			marker = new Sprite();
			marker.graphics.beginFill(0x0, 0.5);
			marker.graphics.drawRect(0, 0, 3, lineHeight);
			marker.graphics.endFill();
			addChild(marker);
			
			markerBlinkTimer = new Timer(600);
			markerBlinkTimer.addEventListener(TimerEvent.TIMER, markerBlink);
			
			lineNumberBackground = new Sprite();
			addChild(lineNumberBackground);
		}
		
		public function drawCaret(beforeCharAtIndex:int):void
		{
			var bounds:Rectangle;
			var markerPos:Number = 0;
			
			if (beforeCharAtIndex == 0)
			{
				// Draw on empty line
			}
			else if (beforeCharAtIndex >= model.text.length)
			{
				bounds = textLine.getAtomBounds(model.text.length-1);
				markerPos = bounds.x+bounds.width;
			}
			else
			{
				bounds = textLine.getAtomBounds(beforeCharAtIndex);
				markerPos = bounds.x;
			}
			
			lastMarkerPosition = markerPos;
			drawMarkerAtPosition(markerPos, 0);
		}
		
		public function drawSelection(start:int, end:int):void
		{
			if (start == end || start < 0) 
			{
				removeSelection();
				return;	
			}
			if (start > end)
			{
				var tmp:int = start;
				start = end;
				end = tmp;
			}
		
			var selStart:int = Math.floor(textLine.getAtomBounds(start).x);
			var endBounds:Rectangle = textLine.getAtomBounds(end-1);
			var selWidth:int = Math.ceil(endBounds.x + endBounds.width) - selStart;
			
			drawSelectionRect(selStart, selWidth);
		}
		
		public function drawFullLineSelection(lineWidth:int, startAtChar:int=0):void
		{
			var start_x:int = 0;
			if (startAtChar > 0)
			{
				start_x = textLine.getAtomBounds(Math.min(startAtChar, model.text.length) - 1).right;
			}
			
			drawSelectionRect(start_x, lineWidth-start_x);
		}
		
		public function removeSelection():void
		{
			selection.graphics.clear();
		}
		
		public function getCharIndexFromPoint(x:int):int
		{
			var pt:Point = this.globalToLocal(new Point(x,0));
			
			if (model.text.length == 0)
			{
				return pt.x >= lineNumberWidth ? 0 : -1;
			}
			else if (pt.x >= textLine.width+lineNumberWidth) // After text 
			{
				return model.text.length;
			}
			else
			{
				// Get a line through the middle of the text field for y
				var mid:Point = this.localToGlobal(new Point(0, lineHeight/2));
				var i:int = textLine.getAtomIndexAtPoint(x, mid.y);	
			
				if (i > -1)
				{
					var bounds:Rectangle = textLine.getAtomBounds(i);
					var center:Number = lineNumberWidth + bounds.x + bounds.width/2;
					// If point falls after the center of the character, move to next one
					if (pt.x >= center) i++;
				}
				
				return i;
			}
		}
		
		private function drawText():void
		{
			if (textLine)
			{
				removeChild(textLine);
			}
			
			var text:String = model.text;
			var meta:Vector.<int> = model.meta;
            var groupElement:GroupElement = new GroupElement();
            var e:Vector.<ContentElement> = new Vector.<ContentElement>();
            
            if (meta)
            {
            	var style:int, start:int, end:int;
            	for (var i:int = 0; i < meta.length; i+=2) 
            	{
	            	start = meta[i];
	            	end = (i+2 < meta.length) ? meta[i+2] : text.length;
	            	style = meta[i+1];
	            	var textElement:TextElement = new TextElement(text.substring(start, end), styles[style]);

	            	e.push(textElement);
            	}
            } 
            else
            {
            	e.push( new TextElement(text, styles[0]) );
            }
            
            groupElement.setElements(e);
   			
            textBlock.tabStops = tabStops;
            textBlock.content = groupElement; 
            textLine = textBlock.createTextLine();
            
            if (textLine) 
            {
            	textLine.mouseEnabled = false;
            	addChildAt(textLine, 1);
            	textLine.x = lineNumberWidth + horizontalOffset;
            	textLine.y = 12;
            }
		}
		
		private function drawLineNumber():void
		{
			if (lineNumberText)
			{
				removeChild(lineNumberText);
			}
			
			if (lineNumberWidth > 0)
			{
				lineNumberBackground.graphics.clear();
				if (model.breakPoint)
				{
					lineNumberBackground.graphics.beginFill(styles['breakPointBackground']);
					lineNumberBackground.graphics.drawRect(0, 0, lineNumberWidth, lineHeight);
					lineNumberBackground.graphics.endFill();
				}
				else
				{
					lineNumberBackground.graphics.beginFill(0xf9f9f9);
					lineNumberBackground.graphics.drawRect(0, 0, lineNumberWidth, lineHeight);
					lineNumberBackground.graphics.endFill();
				}
				
				var style:ElementFormat = (model.breakPoint) ? styles['breakPointLineNumber'] : styles['lineNumber'];
				var t:TextElement = new TextElement((_dataIndex+1).toString(), style);
				lineNumberTextBlock.content = t;
				lineNumberText = lineNumberTextBlock.createTextLine(null, lineNumberWidth);
				
				if (lineNumberText) 
				{
					lineNumberText.mouseEnabled = false;
					lineNumberText.mouseChildren = false;
					lineNumberText.y = 12;
					lineNumberText.x = lineNumberWidth-lineNumberText.width-3;
					addChild(lineNumberText);
				}
			}
		}
		
		private function drawSelectionRect(x:int, w:int):void
		{
			var g:Graphics = selection.graphics;
			g.clear();
			g.beginFill(styles['selectionColor']);
			g.drawRect(x, 0, w, lineHeight);
			g.endFill();
		}
		
		private function drawMarkerAtPosition(x:int, y:int):void
		{
			x += lineNumberWidth + _horizontalOffset;
			marker.x = x;
			marker.y = y;
			
			if (focus)
			{
				markerBlinkTimer.reset();
				markerBlinkTimer.start();
				marker.visible = true;
			}
		}
		
		private function markerBlink(event:TimerEvent):void
		{
			marker.visible = !marker.visible;
		}

	}
}